#ifndef CENGINE_H
#define CENGINE_H

#include <QObject>
#include <QDeclarativeEngine>
#include "ccuisine.h"
#include "cingredient.h"
#include "cmethod.h"
#include "crecipe.h"
#include "cregion.h"
#include "ctechnique.h"
#include "ccuisinemodel.h"
#include "crecipemodel.h"
#include "cflavour.h"
#include "cdishtype.h"
#include "cfoodtype.h"
#include "crecipeingredient.h"
#include "cunittype.h"
#include "ctip.h"
#include "cconsumenutrition.h"
#include "cnutritionconsume.h"
#include "cnutrition.h"
#include "cobject.h"
#include "cingredientcategory.h"
#include "ccuisinerecipe.h"
#include "cingredientnutrition.h"
#include "cshoppinglist.h"
#include "clistitem.h"
namespace cooking {

class CEngine : public QObject
{
    Q_OBJECT
public:
    CEngine();
    virtual ~CEngine();
    /**
     * Get cuisine by cuisine id
     * @param cuisineId cuisine id
     * @return CCuisine*, valid is true if found or false
     */
    Q_INVOKABLE QObject* cuisine(int cuisineId);

    /**
     * Add or udpate cuisine
     * @param cuisine CCuisine* object, ownership is not transferred
     * @return added or udpated cuisine id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateCuisine(QObject* cuisine);

    /**
     * Delete cuisine by ID
     * @param cuisineId cuisine Id
     */
    Q_INVOKABLE void deleteCuisine(int cuisineId);

    /**
     * Get cuisine recipe by cuisine recipe id
     * @param cuisineRecipeId cuisine recipe table row id
     * @return CCuisineRecipe*, valid is true if found or false
     */
    Q_INVOKABLE QObject* cuisineRecipe(int cuisineRecipeId);

    /**
     * Add or udpate cuisine recipe
     * @param cuisineRecipe CCuisineRecipe* object, ownership is not transferred
     * @return added or udpate cuisine recipe id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateCuisineRecipe(QObject* cuisineRecipe);

    /**
     * Delete cuisine recipe by ID
     * @param cuisineRecipeId cuisine recipe Id
     */
    Q_INVOKABLE void deleteCuisineRecipe(int cuisineRecipeId);

    /**
     * Get region by region id
     * @param regionId region id
     * @return CRegion*, valid is true if found or false
     */
    Q_INVOKABLE QObject* region(int regionId);

    /**
     * Add or udpate region
     * @param region CRegion* object, ownership is not transferred
     * @return added or udpate region id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateRegion(QObject* region);

    /**
     * Delete region by ID
     * @param regionId region Id
     */
    Q_INVOKABLE void deleteRegion(int regionId);

    /**
     * Get ingredient by ingredient id
     * @param ingredientId ingredient id
     * @return CIngredient*, valid is true if found or false
     */
    Q_INVOKABLE QObject* ingredient(int ingredientId);

    /**
     * Add or udpate ingredient
     * @param ingredient CIngredient* object, ownership is not transferred
     * @return added or udpated ingredient id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateIngredient(QObject* ingredient);

    /**
     * Delete ingredient by ID
     * @param ingredientId ingredient Id
     */
    Q_INVOKABLE void deleteIngredient(int ingredientId);

    /**
     * Get ingredient nutrition by ingredient nutrition id
     * @param ingredientId ingredient id
     * @return CIngredientNutrition*, valid is true if found or false
     */
    Q_INVOKABLE QObject* ingredientNutrition(int ingredientNutritionId);

    /**
     * Get ingredient nutrition list by ingredient id
     * @param ingredientId ingredient id
     * @return QObjectList with QObject type of CIngredientNutrition
     */
    Q_INVOKABLE QObjectList ingredientNutritions(int ingredientId);

    /**
     * Add or udpate ingredient nutrition
     * @param ingredientNutrition CIngredientNutrtion* object, ownership is not transferred
     * @return added or udpated ingredient nutrition id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateIngredientNutrition(QObject* ingredientNutrition);

    /**
     * Delete ingredient nutrition by ID
     * @param ingredientNutritionId ingredient nutrition Id
     */
    Q_INVOKABLE void deleteIngredientNutrition(int ingredientNutritionId);

    /**
     * Get ingredient by ingredient id
     * @param ingredientCatId ingredient category id
     * @return CIngredientCategory*, valid is true if found or false
     */
    Q_INVOKABLE QObject* ingredientCategory(int ingredientCatId);

    /**
     * Add or udpate ingredient
     * @param ingredientCat CIngredientCategory* object, ownership is not transferred
     * @return added or udpated ingredient category id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateIngredientCategory(QObject* ingredientCat);

    /**
     * Delete ingredient category by ID
     * @param ingredientCategoryId ingredient category Id
     */
    Q_INVOKABLE void deleteIngredientCategory(int ingredientCategoryId);

    /**
     * Get recipe ingredient list by recipe id
     * @param recipeId recipeId id
     * @return QObjectList with QObject type of CRecipeIngredient
     */
    Q_INVOKABLE QObjectList recipeIngredients(int recipeId);
    //    Q_INVOKABLE void deleIngredient(int regredientId);

    /**
     * Get recipe ingredient by recipe ingredient id
     * @param recipeIngredientId recipe ingredient id
     * @return CRecipeIngredientId*, valid is true if found or false
     */
    Q_INVOKABLE QObject* recipeIngredient(int recipeIngredientId);

    /**
     * Add or udpate recipe ingredient
     * @param recipeIngredient CRecipeIngredient* object, ownership is not transferred
     * @return added or udpate recipe ingredient id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateRecipeIngredient(QObject* recipeIngredient);

    /**
     * Delete recipe ingredient by ID
     * @param recipeIngredientId recipe ingredient Id
     */
    Q_INVOKABLE void deleteRecipeIngredient(int recipeIngredientId);

    /**
     * Get method list by recipe id
     * @param recipeId recipeId id
     * @return QObjectList with QObject type of CMethod
     */
    Q_INVOKABLE QObjectList methods(int recipeId);

    /**
     * Get method by method id
     * @param methodId method id
     * @return CMethod*, valid is true if found or false
     */
    Q_INVOKABLE QObject* method(int methodId);

    /**
     * Get method by recipe id and step
     * @param recipeId recipeId id
     * @param step step of the method
     * @return CMethod*, valid is true if found or false
     */
    Q_INVOKABLE QObject* method(int recipeId, int step);
    //    Q_INVOKABLE void delMethod(int methodId);

    /**
     * Add or udpate method
     * @param method CMethod* object, ownership is not transferred
     * @return added or udpated method id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateMethod(QObject* method);

    /**
     * Delete method by ID
     * @param methodId method Id
     */
    Q_INVOKABLE void deleteMethod(int methodId);

    /**
     * Get tip list by recipe id
     * @param recipeId recipeId id
     * @return QObjectList with QObject type of CTip
     */
    Q_INVOKABLE QObjectList tips(int recipeId);

    /**
     * Get tip by tip id
     * @param tipId tip id
     * @return CTip*, valid is true if found or false
     */
    Q_INVOKABLE QObject* tip(int tipId);

    /**
     * Add or udpate tip
     * @param tip CTip* object, ownership is not transferred
     * @return added or udpated tip id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateTip(QObject* tip);

    /**
     * Delete tip by ID
     * @param tipId tip Id
     */
    Q_INVOKABLE void deleteTip(int tipId);

    /**
     * Get flavour by flavour id
     * @param flavourId flavour id
     * @return CFlavour*, valid is true if found or false
     */
    Q_INVOKABLE QObject* flavour(int flavourId);

    /**
     * Add or udpate flavour
     * @param flavour CFlavour* object, ownership is not transferred
     * @return added or udpated flavour id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateFlavour(QObject* flavour);

    /**
     * Delete flavour by ID
     * @param flavourId flavour Id
     */
    Q_INVOKABLE void deleteFlavour(int flavourId);

    /**
     * Get food type by food type id
     * @param foodTypeId food type id
     * @return CFoodType*, valid is true if found or false
     */
    Q_INVOKABLE QObject* foodType(int foodTypeId);

    /**
     * Add or udpate food type
     * @param foodType CFoodType* object, ownership is not transferred
     * @return added or udpated food type id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateFoodType(QObject* foodType);

    /**
     * Delete food type by ID
     * @param foodType food type Id
     */
    Q_INVOKABLE void deleteFoodType(int foodTypeId);

    /**
     * Get dish type by dish type id
     * @param dishTypeId dish type id
     * @return CDishType*, valid is true if found or false
     */
    Q_INVOKABLE QObject* dishType(int dishTypeId);

    /**
     * Add or udpate dish type
     * @param dishType CDishType* object, ownership is not transferred
     * @return added or udpated dish type id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateDishType(QObject* dishType);

    /**
     * Delete dish type by ID
     * @param dishTypeId dish type Id
     */
    Q_INVOKABLE void deleteDishType(int dishTypeId);

    /**
     * Get recipe by recipe id
     * @param recipeId recipe id
     * @return CRecipe*, valid is true if found or false
     */
    Q_INVOKABLE QObject* recipe(int recipeId);


    /**
     * Add or udpate recipe
     * @param recipe CRecipe* object, ownership is not transferred
     * @return added or udpated recipe id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateRecipe(QObject* recipe);

    /**
     * Delete recipe by ID
     * @param recipeId recipe Id
     */
    Q_INVOKABLE void deleteRecipe(int recipeId);

    /**
     * Get technique by technique id
     * @param techniqueId technique id
     * @return CTechnique*, valid is true if found or false
     */
    Q_INVOKABLE QObject* technique(int techniqueId);


    /**
     * Add or udpate technique
     * @param technique CTechnique* object, ownership is not transferred
     * @return added or udpated technique id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateTechnique(QObject* technique);

    /**
     * Delete technique by ID
     * @param techniqueId technique Id
     */
    Q_INVOKABLE void deleteTechnique(int techniqueId);

    /**
     * Get unit type by unit type id
     * @param unitTypeId unit type id
     * @return CUnitType*, valid is true if found or false
     */
    Q_INVOKABLE QObject* unitType(int unitTypeId);

    /**
     * Add or udpate unit type
     * @param unitType CUnitType* object, ownership is not transferred
     * @return added or udpated unit type id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateUnitType(QObject* unitType);

    /**
     * Delete unit type by ID
     * @param unitType unit type Id
     */
    Q_INVOKABLE void deleteUnitType(int unitTypeId);

    /**
     * Get nutrition by nutrition id
     * @param nutritionId nutrition id
     * @return CNutrition*, valid is true if found or false
     */
    Q_INVOKABLE QObject* nutrition(int nutritionId);

    /**
     * Add or udpate nutrition
     * @param nutrition CNutrition* object, ownership is not transferred
     * @return added or udpated nutrition id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateNutrition(QObject* nutrition);

    /**
     * Delete nutrition by ID
     * @param nutritionId nutrition Id
     */
    Q_INVOKABLE void deleteNutrition(int nutritionId);

    /**
     * Get nutrition consume by nutritionConsume id
     * @param nutritionConsumeId nutrition consume id
     * @return CNutritionConsume*, valid is true if found or false
     */
    Q_INVOKABLE QObject* nutritionConsume(int nutritionConsumeId);

    /**
     * Add or udpate nutrition consume
     * @param nutritionConsume CNutritionConsume* object, ownership is not transferred
     * @return added or udpated nutrition consume id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateNutritionConsume(QObject* nutritionConsume);

    /**
     * Delete nutrition consume by ID
     * @param nutritionConsumeId nutrition consume Id
     */
    Q_INVOKABLE void deleteNutritionConsume(int nutritionConsumeId);

    /**
     * Get consume nutrition by nutrition consume id
     * @param nutritionConsumeId nutrition consume id
     * @return QObjectList with QObject type of CConsumeNutrition
     */
    Q_INVOKABLE QObjectList consumeNutrition(int nutritionConsumeId);

    /**
     * Add or udpate consume nutrition
     * @param consumeNutrition CConsumeNutrition* object, ownership is not transferred
     * @return added or updated consume nutrition id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateConsumeNutrition(QObject* consumeNutrition);

    /**
     * Delete consume nutritionId by ID
     * @param consumeNutritionId consume nutritionId Id
     */
    Q_INVOKABLE void deleteConsumeNutrition(int consumeNutritionId);

    /**
     * Get related cuisine model by parent cuisine id. The
     * returned cuisine model is singleton.
     * @param parentId parent cuisine id, -1 for root cuisine
     * @return CCuisineModel*
     */
    Q_INVOKABLE QObject *cuisineModel(int parentId);

    /**
     * Get recipe model by cuisine id. The returned recipe model
     * is singleton
     * @param cuisineId Parent cuisine id
     * @return CRecipeModel*
     */
    Q_INVOKABLE QObject *recipeModel(int cuisineId);

    /**
     * Test whether child cuisine is available
     * @param cuisineId Cuisine Id
     * @return true if has child cuisine else false
     */
    Q_INVOKABLE bool hasChildCuisine(int cuisineId);

    /**
     * Get child cuisine count
     * @param cuisineId Cuisine Id
     * @return Child cuisine count
     */
    Q_INVOKABLE int childCuisineCount(int cuisineId);


    /**
     * Get shopping lists
     * @return QObjectList with QObject type of CShoppingList
     */
    Q_INVOKABLE QObjectList shoppingLists();

    /**
     * Get shopping list by list id
     * @param listId list id
     * @return CShoppingList*, valid is true if found or false
     */
    Q_INVOKABLE QObject* shoppingList(int listId);

    /**
     * Add or update shopping list
     * @param list CShoppingList* object, ownership is not transferred
     *             if object id is not KInvalidId, update or add
     * @return added or updated shopping list id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateShoppingList(QObject* list);

    /**
     * Delete shopping list by ID
     * @param shoppingListId shopping list Id
     */
    Q_INVOKABLE void deleteShoppingList(int shoppingListId);

    /**
     * Get shopping lists item list by shopping list id,
     * the item is summarized, and the id of CListItem
     * is invalid, you can not rely on the id to get the CListItem
     * @param listId shopping list id
     * @return QObjectList with QObject type of CListItem
     */
    Q_INVOKABLE QObjectList listItems(int listId);

    /**
     * Get shopping list item by list item id
     * @param listItemId list item id
     * @return CListItem*, valid is true if found or false
     */
    Q_INVOKABLE QObject* listItem(int listItemId);

    /**
     * Add or update shopping list item
     * @param listItem CListItem* object, ownership is not transferred
     * @return added or updated shopping list ingredient id, > 0 if success or -1
     */
    Q_INVOKABLE int addUpdateListItem(QObject* listItem);

    /**
     * Delete list item by ID
     * @param listItemId list item Id
     */
    Q_INVOKABLE void deleteListItem(int listItemId);

private:
    QString pictureFolderPath();
    QString installPath();
    void fillMethod(CMethod& method, QSqlQuery& sqlQuery);

    class InsertUpdateHelper {
    public:
        InsertUpdateHelper(const QString& tableName);
        void setTable(const QString& tableName);
        void setId(const QString& idColumn, int id);
        bool insertSql(QString& sqlString);
        bool updateSql(QString& sqlString);
        void addNull(const QString& column);
        void addValue(const QString& column, const QString& value);
        void addValue(const QString& column, const int& value);
        void addValue(const QString& column, const double& value);
        void addValue(const QString& column, const bool& value);
    private:
        QString m_table_name;
        QMap<QString, QString> m_column_value;
        QString m_where;
    };

private:
    /**
     * Query data specified by sqlStr
     * @param sqlStr SELECT or INSERT like SQL string
     * @param nonSelect false for SELECT string, true for UPDATE, INSTERT like string
     * @return QSqlQuery* if find data or NULL
     */
    QSqlQuery *query(QString &sqlStr, bool nonSelect = false);
    int initDb();
    void addDummyData();
    bool createCuisineTable();
    bool createRecipeTable();
    bool createRegionTable();
    bool createFlavourTable();
    bool createIngredientTable();
    bool createTechniqueTable();
    bool createDishTypeTable();
    bool createFoodTypeTable();
    bool createMethodTable();
    bool createRecipeIngredientTable();
    bool createUnitTypeTable();
    bool createCuisineRecipeTable();
    bool createNutritionTable();
    bool createIngredientNutritionTable();
    bool createNutritionConsumeTable();
    bool createConsumeNutritionTable();
    bool createMealTable();
    bool createMealRecipeTable();
    bool createTipsTable();
    bool createIngredientCategoryTable();
    bool createShoppingListTable();

    bool createListItemTable();
    void fillListItem(CListItem& item, const QSqlQuery& query);

    bool createTable(QString &sql);
    QString DatabaseName();
    int addUpdateObject(const QString& tableName,
                        const CObject& obj,
                        const QMap<QString, QVariant>& columnValue);

signals:
    //    void cuisineSaved()

private:
    //    CRegion* m_newRegion;
    //    CCuisineModel *m_cuisineModel;
    //    CRecipeModel *m_recipeModel;
    //    CCuisine *m_cuisine;
    //    CRecipe *m_recipe;
};
} // namespace cooking
#endif // CENGINE_H
